import java.util.HashMap;
import java.util.ArrayList;
public class Simbolo {

    public String nombre;   
    public int posicion;                                                            // si el simbolo es una variable que debe aparecer en locals, tiene posicion
    private int numargs=1;
    public boolean emprat;
    public ArrayList dimensiones;                                       // si el simbolo es un array, tiene un array de dimensiones 
                                                                                                    // en cada item se indican los elementos de cada dimension
    public HashMap<String, Simbolo> argumentos; // si es un metodo tiene un array de argumentos
    public String nomClase;                                                 // si es metodo o campo, pertenece a esta clase
    public TablaSimbolos tSimbolos;                             // si es una clase o un metodo, tiene una tabla de simbolos
    public TablaSimbolos tPadre;   
    public boolean indice = false       ;                          // las tablas de simbolos que no tienen padre son las de las clases, el resto si
    public boolean publico=true;                                                 // para campos
    public boolean requiereRetorno=true;                    // si el metodo no es un constructor, requiereRetorno es true
    public boolean encontroCtorPorDefecto=false; // lo usamos si el simbolo es clase
 



    public int tipoSimbolo;
    // Clases de simbolos:
    public static final int
            VAR = 1,
            METODO = 2,
            CLASE = 3,
            OBJETO = 4;

    public int tipo;
    // Tipos de datos:
    public static final int
            ESPECIAL = -1,
            ENTERO = 1,
            REAL = 2,
            BOOLEANO = 3,
            NO_MAIN = 5,
            MAIN = 6,
            CONSTRUCTOR = 7;



    public static String devuelveTipoCadena(int tipo){

        switch (tipo){
            case 1:
                return "int";
            case 2:
                return "double";
            case 3:
                return "bool";
            default:
                return "Fuuuuuuuuuuuuuuuuuuuuu";

        }
    }

    public String devuelveTipoNazi(){
        switch (tipo){
            case 1:
                return "Int32";
            case 2:
                return "Float64";
            case 3:
                return "Int32";
            case 4:
                return "object";
            default:
                return "yesWeCamp";
        }
    }

    public Simbolo(String nombre, int tipoSimbolo, int tipo, int posicion) {
        this.nombre = nombre;
        this.tipoSimbolo = tipoSimbolo;
        this.tipo = tipo;
        this.posicion= posicion;
        if (tipoSimbolo == CLASE || tipo == MAIN) {
            this.emprat = true;
        } else {
            this.emprat = false;
        }

        this.dimensiones= new ArrayList();
        this.argumentos = new HashMap<String, Simbolo>();
        this.nomClase = new String();
      
        this.tSimbolos = new TablaSimbolos(null,this);
       
        
    }
    
        public Simbolo(String nombre, int tipoSimbolo, int tipo, int posicion, boolean esIndice) {
        this.nombre = nombre;
        this.tipoSimbolo = tipoSimbolo;
        this.tipo = tipo;
        this.posicion= posicion;
        if (tipoSimbolo == CLASE || tipo == MAIN) {
            this.emprat = true;
        } else {
            this.emprat = false;
        }
	   this.indice = esIndice;
        this.dimensiones= new ArrayList();
        this.argumentos = new HashMap<String, Simbolo>();
        this.nomClase = new String();
      
        this.tSimbolos = new TablaSimbolos(null,this);
       
        
    }

        // Constructor para vectores
     public Simbolo(String nombre, int tipoSimbolo, int tipo, int posicion, int dims) {
        this.nombre = nombre;
        this.tipoSimbolo = tipoSimbolo;
        this.tipo = tipo;
        this.posicion= posicion;
        if (tipoSimbolo == CLASE || tipo == MAIN) {
            this.emprat = true;
        } else {
            this.emprat = false;
        }
        this.dimensiones= new ArrayList();
        for(int i=0; i< dims; i++){
            dimensiones.add(0);
        }
        this.nomClase = new String();
        this.argumentos = new HashMap<String, Simbolo>();
        this.tSimbolos = new TablaSimbolos(null,this);
    }
// contructor para objetos
     public Simbolo(String nombreObjeto, String claseALaQuePertenece, int posicion){
         this.nombre = nombreObjeto;
         this.nomClase = claseALaQuePertenece;
         this.tipoSimbolo = OBJETO;
         this.tipo = ESPECIAL;
         this.posicion = posicion;
         this.emprat = false;
         this.dimensiones= new ArrayList();
         this.argumentos = new HashMap<String, Simbolo>();
         this.tipoSimbolo = OBJETO;
         this.tSimbolos = new TablaSimbolos(null,this);
     }


    public Simbolo(Simbolo sim) {
        this.nombre = sim.nombre;
        this.tipoSimbolo = sim.tipoSimbolo;
        this.tipo = sim.tipo;
        this.emprat = sim.emprat;
        this.posicion = sim.posicion;
        this.dimensiones = sim.dimensiones;
        this.nomClase = sim.nomClase;
        this.argumentos = sim.argumentos;
        this.tSimbolos = sim.tSimbolos;
        this.tPadre = sim.tPadre;
    }

    /*Da igual el numero que incluyas en el simbolo*/
    public void anyadeArgumento(Simbolo argumento){
        argumento.posicion=numargs;
        numargs++;
        argumentos.put(argumento.getNombre(), argumento);
    }

    public boolean estaArgumento(String nombreArgumento){
        return(argumentos.containsKey(nombreArgumento));    }

    public int getNumArgumento(String nomArg){
        return(argumentos.get(nomArg).getPosicion());    }

    public Simbolo getArgumento(String nomArg){
        return(argumentos.get(nomArg));    }
    
    public boolean esIndice(){
    		return indice;
    	}
    	
    	public void quitaIndice(){
    		indice = false;
    	
    	}
    
     public int getNumArgs(){
        if (argumentos==null){     return 0;
        } else {  return argumentos.size(); }
     } 
     
     public void anyadeTablaSimbolos(TablaSimbolos novaTabla)    {        this.tSimbolos = novaTabla;    }
    public boolean tieneTablaSimbolos()                                                      {        return tSimbolos != null;    }
    public TablaSimbolos getTablaSimbolos()                                             {        return tSimbolos;    }
    
    public void setNomClasse(String nouNom){        this.nomClase = nouNom;    }
    public String getNomClasse()                            {        return(this.nomClase);    }

    public void setPosicion(int pos)    {        posicion = pos; }
    public int getPosicion()                    {        return(posicion); }

    public void setPublico()                     {        publico = true;}
    public void setPrivado()                    {        publico = false;}
    public boolean esPublico()              {        return publico;}
    public boolean esPrivado()              {        return !publico;}

    public boolean esEmprat()   {        return emprat == true;}
     public void setEmprat()        {        emprat = true;}
     
     public String getNombre() {        return nombre;}

    public boolean esObjeto()              {        return(tipoSimbolo == OBJETO);    }
    public boolean esVAR()                   {        return tipoSimbolo == VAR;    }
    public boolean esCLASE()               {        return tipoSimbolo == CLASE;    }
    
    public boolean esEntero()              {        return tipo == ENTERO;    }
    public boolean esReal()                   {        return tipo == REAL;    }
    public boolean esBooleano()       {        return tipo == BOOLEANO;    }

    public boolean esMETODO()       {        return tipoSimbolo == METODO;    }
    public boolean esConstructor()  {        return tipo == CONSTRUCTOR;    }
    public boolean esMain()                  {        return tipo == MAIN;    }    
    public int getRetorno()                     {        return tipo;    }    // Devuelve el tipo que devuelve un metodo
    public void setRequiereRetorno(boolean requiere)    {        this.requiereRetorno = requiere;    }
    public boolean requiereRet()                                                     {        return this.requiereRetorno;    }
    
      public boolean esArray()                 {        return (dimensiones.size()!=0);    }
      public int getTamanyoArray()     {        return(dimensiones.size());    }
           int obtenDimensionTotal()    {        return getDimParcial(0);    }    //  elementos totales de un vector, lo usamos para reservar memoria   
     
       public boolean esCampo(){
            if(tPadre!=null){
                if(tPadre.sPadre!=null){
                    return(tPadre.sPadre.esCLASE() && tipoSimbolo!=METODO ); }
            }      return false;
        }
      
    // la usamos cuando reservamos memoria (new) para establecer el tamanyo de las dimensiones
    public boolean setOrra(ArrayList params) {
        if(params.size()!=dimensiones.size()){
            return false;
        } else {
            dimensiones = params;
            return true; }
    }
    // le paso un entero que me dice la dimension del resto de vector
    public int getDimParcial(int n)
    {
        int sal=1;
        if (dimensiones.size()>0)
        {
            for(int i=n; i< dimensiones.size(); i++){
                sal = (Integer)dimensiones.get(i)*sal; }  }
        return sal;
    }

    @Override
    public String toString() {
        String sal = "(" + nombre + " , " + tipoSimbolo + " , " + tipo + " , " + posicion + " , ";
        if (esEmprat()) {
            sal += "emprat";
        }
        return sal += ")";
    }
    
}

